package org.yinxianren.java.test.thread;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 并发应用的一个关键地方就是共享数据。这个对那些扩展Thread类或者实现Runnable接口的对象特别重要。
 * 如果你创建一个类对象，实现Runnable接口，然后多个Thread对象使用同样的Runnable对象，全部的线程都共享同样的属性。
 * 这意味着，如果你在一个线程里改变一个属性，全部的线程都会受到这个改变的影响。
 * 有时，你希望程序里的各个线程的属性不会被共享。 Java 并发 API提供了一个很清楚的机制叫本地线程变量。
 */
public class UnsafeTask implements Runnable{
    private Date startDate;

    @Override
    public void run() {
        startDate=new Date();
        System.out.printf("Starting Thread: %s : %s\n",Thread. currentThread().getId(),startDate);
        try {
            TimeUnit.SECONDS.sleep( (int)Math.rint(Math.random()*10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("Thread Finished: %s : %s\n",Thread. currentThread().getId(),startDate);
    }
}
/*
Starting Thread: 12 : Sun May 12 19:47:27 CST 2019
Thread Finished: 12 : Sun May 12 19:47:27 CST 2019
Starting Thread: 13 : Sun May 12 19:47:29 CST 2019
Starting Thread: 14 : Sun May 12 19:47:31 CST 2019
Thread Finished: 14 : Sun May 12 19:47:31 CST 2019
Starting Thread: 15 : Sun May 12 19:47:33 CST 2019
Starting Thread: 16 : Sun May 12 19:47:35 CST 2019
Thread Finished: 13 : Sun May 12 19:47:35 CST 2019
Starting Thread: 17 : Sun May 12 19:47:37 CST 2019
Thread Finished: 16 : Sun May 12 19:47:37 CST 2019
Starting Thread: 18 : Sun May 12 19:47:39 CST 2019
Starting Thread: 19 : Sun May 12 19:47:41 CST 2019
Thread Finished: 15 : Sun May 12 19:47:41 CST 2019
Starting Thread: 20 : Sun May 12 19:47:43 CST 2019
Starting Thread: 21 : Sun May 12 19:47:45 CST 2019
Thread Finished: 17 : Sun May 12 19:47:45 CST 2019
Thread Finished: 19 : Sun May 12 19:47:45 CST 2019
Thread Finished: 18 : Sun May 12 19:47:45 CST 2019
Thread Finished: 20 : Sun May 12 19:47:45 CST 2019
Thread Finished: 21 : Sun May 12 19:47:45 CST 2019
 */
class Core {
    public static void main(String[] args) {
//        UnsafeTask task=new UnsafeTask();
        SafeTask task=new SafeTask();
        for (int i=0; i<10; i++){
            Thread thread=new Thread(task);
            thread.start();
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
/*
Starting Thread: 12 : Sun May 12 20:07:25 CST 2019
Starting Thread: 13 : Sun May 12 20:07:27 CST 2019
Thread Finished: 12 : Sun May 12 20:07:25 CST 2019
Starting Thread: 14 : Sun May 12 20:07:29 CST 2019
Starting Thread: 15 : Sun May 12 20:07:31 CST 2019
Starting Thread: 16 : Sun May 12 20:07:33 CST 2019
Thread Finished: 15 : Sun May 12 20:07:31 CST 2019
Starting Thread: 17 : Sun May 12 20:07:35 CST 2019
Thread Finished: 13 : Sun May 12 20:07:27 CST 2019
Thread Finished: 17 : Sun May 12 20:07:35 CST 2019
Starting Thread: 18 : Sun May 12 20:07:37 CST 2019
Thread Finished: 14 : Sun May 12 20:07:29 CST 2019
Starting Thread: 19 : Sun May 12 20:07:39 CST 2019
Thread Finished: 16 : Sun May 12 20:07:33 CST 2019
Starting Thread: 20 : Sun May 12 20:07:41 CST 2019
Thread Finished: 18 : Sun May 12 20:07:37 CST 2019
Starting Thread: 21 : Sun May 12 20:07:43 CST 2019
Thread Finished: 21 : Sun May 12 20:07:43 CST 2019
Thread Finished: 19 : Sun May 12 20:07:39 CST 2019
Thread Finished: 20 : Sun May 12 20:07:41 CST 2019
 */
class SafeTask implements Runnable {
//地线程变量为每个使用这些变量的线程储存属性值。可以用 get() 方法读取值和使用 set() 方法改变值。
// 如果第一次你访问本地线程变量的值，如果没有值给当前的线程对象，
// 那么本地线程变量会调用 initialValue() 方法来设置值给线程并返回初始值。

//本地线程类还提供 remove() 方法，删除存储在线程本地变量里的值。
//Java 并发 API 包括 InheritableThreadLocal 类提供线程创建线程的值的遗传性 。
// 如果线程A有一个本地线程变量，然后它创建了另一个线程B，那么线程B将有与A相同的本地线程变量值。
// 你可以覆盖 childValue() 方法来初始子线程的本地线程变量的值。 它接收父线程的本地线程变量作为参数。
    private static ThreadLocal<Date> startDate= new ThreadLocal<Date>() {
        protected Date initialValue(){
            return new Date();
        }
    };

    @Override
    public void run() {
        System.out.printf("Starting Thread: %s : %s\n",Thread.currentThread().getId(),startDate.get());
        try {
            TimeUnit.SECONDS.sleep((int)Math.rint(Math.random()*10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("Thread Finished: %s : %s\n",Thread.currentThread().getId(),startDate.get());
    }
}